Skip to content

fix(ai-chat): say what happened when a provider goes, a tool call is proposed, or an image will not attach - #3018

Merged
datlechin merged 1 commit into
mainfrom
fix/ai-chat-defects
Sep 20, 2026
Merged

datlechin merged 1 commit into
mainfrom
fix/ai-chat-defects

Conversation

@datlechin

Copy link
Copy Markdown
Member

Follows #3010, which merged while this was in verification; this now targets main directly.

Four defects found while investigating #2995, all in TablePro/Views/AIChat/, plus the design-rule cleanup in the same folder. None of them is the reported bug, so each was verified on its own before it was fixed.

The assistant pane went dead when the provider went

AIChatPanelView showed the "AI Not Configured" empty state only when the transcript was also empty, while the composer, model picker and send button were gated on having a provider alone. So the state "transcript, no provider" fell between them: the pane kept the conversation and dropped everything below it, with no reason given and no route to Settings, because the Go to Settings… button lives on the empty-transcript branch.

Both ways in are ordinary: setting Active Provider to None, or removing the provider that was active. Now that state gets a footer saying the conversation is read-only, with a Settings… button.

Every Run button was announced identically

ToolApprovalActionsRow already carried the rule in its own comment:

Each button names its own call. A turn proposing three writes otherwise hands assistive clients three identical "Run" buttons with nothing to tell them apart.

It was applied to Reject and Always Allow and missed on Run, which is the one that executes the statement and owns .defaultAction. A turn proposing three writes gave VoiceOver three buttons all reading "Run". Now it reads "Run toolName", like its neighbours.

A mixed image drop discarded files in silence

handleDrop collected successes, kept only the last error, and reported it only when nothing succeeded. Drop one PNG and two files that fail to convert and you got one thumbnail, no error, and a prompt that went to the model referring to images it never received.

ChatImageDropReport is the answer, as a pure function so it can be tested: it stays silent when nothing failed, passes a lone failure through verbatim, and otherwise names how many landed out of how many were offered.

An unreadable pasted image became a file path

paste(_:) folded "is this an image file" and "can I read it" into one if let:

if let urls = ..., let fileURL = urls.first(where: { ...image... }),
   let data = try? Data(contentsOf: fileURL) {

A read that throws (an iCloud file still in the cloud, a network volume that went away) failed the whole chain and fell through to super.paste(sender), which pasted the file's path as text into the prompt. The two questions are now separate, and a read failure is reported instead of silently becoming text.

The read is still synchronous on the main thread. That is unchanged from what ships and is a separate concern: making it async changes when super.paste runs, which deserves its own change rather than riding along here.

Middle dots

Six · separators in this folder, which the project's design rule names first. The two in HStacks were deleted and the spacing widened; the transcript's token counts take a comma; the slash-command list and model picker use parentheses, since NSMenu draws those titles itself and will not honour a richer layout.

23 more remain elsewhere (Quick Switcher, autocomplete detail strings, license presentation, the workspace rail). They are untouched here because that is a different surface and a bigger call than this PR should make.

Verification

All through .claude/skills/fix-issue/scripts/verify.sh, on a worktree at this commit.

  • generate PASS, build PASS
  • test PASS: 21 executed, 21 passed, 0 failed over ChatImageDropReportTests, ChatComposerScrollViewTests, ChatComposerTextViewAccessibilityTests, ComposerHighlightPreferenceTests
  • lint 0 violations, docs PASS

No UI automation. Every one of these needs a configured AI provider before the view renders at all (AIChatPanelView.swift gates the input area on hasConfiguredProvider), and the UI-test sandbox has none.

The accessibility fixes are the two that cannot be checked by looking: the Run label and the dead pane both need VoiceOver or the Accessibility Inspector to confirm on screen.

@mintlify

mintlify Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
TablePro 🟢 Ready View Preview Sep 20, 2026, 12:30 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@datlechin
datlechin merged commit 7e4720f into main Sep 20, 2026
10 of 13 checks passed
@datlechin
datlechin deleted the fix/ai-chat-defects branch September 20, 2026 15:13

This branch was successfully deployed

1 active deployment
staging - docs — 0350c577 Deployed Sep 20, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant